/*
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package au.com.domlogic.pvc.core.document;

import au.com.domlogic.pvc.core.node.CompositeNode;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;

/**
 * A simple implementation of {@link Document} by Composition.
 *
 * @author Domenic Figliomeni
 */
public abstract class CompositeDocument extends CompositeNode implements Document {

    protected CompositeDocument() {
    }

    public CompositeDocument(Document aDocument) {
        super(aDocument);
    }

    public abstract String getIdAttrName();

    protected Document getUnderlyingDocument() {
        return (Document) this.getUnderlyingNode();
    }

    protected void setUnderlyingDocument(Document document) {
        super.setUnderlyingNode(document);
    }

    /**
     * If arg0 is a CompositeNode, then we need to use the underlying node
     * as it is the one with the same implementation as the document.
     *
     * @see Document#adoptNode(org.w3c.dom.Node)
     */
    @Override
    public Node adoptNode(Node arg0) throws DOMException {
        return this.getUnderlyingDocument().adoptNode(
                CompositeNode.getRealNode(arg0));
    }

    /**
     *
     * @see Document#createAttribute(java.lang.String)
     */
    @Override
    public Attr createAttribute(String arg0) throws DOMException {
        return this.getUnderlyingDocument().createAttribute(arg0);
    }

    /**
     *
     * @see Document#createAttributeNS(java.lang.String, java.lang.String)
     */
    @Override
    public Attr createAttributeNS(String arg0, String arg1) throws DOMException {
        return this.getUnderlyingDocument().createAttributeNS(arg0, arg1);
    }

    /**
     *
     * @see Document#createCDATASection(java.lang.String)
     */
    @Override
    public CDATASection createCDATASection(String arg0) throws DOMException {
        return this.getUnderlyingDocument().createCDATASection(arg0);
    }

    /**
     *
     * @see Document#createComment(java.lang.String)
     */
    @Override
    public Comment createComment(String arg0) {
        return this.getUnderlyingDocument().createComment(arg0);
    }

    /**
     *
     * @see Document#createDocumentFragment()
     */
    @Override
    public DocumentFragment createDocumentFragment() {
        return this.getUnderlyingDocument().createDocumentFragment();
    }

    /**
     *
     * @see Document#createElement(java.lang.String)
     */
    @Override
    public Element createElement(String arg0) throws DOMException {
        return this.getUnderlyingDocument().createElement(arg0);
    }

    /**
     *
     * @see Document#createElementNS(java.lang.String, java.lang.String)
     */
    @Override
    public Element createElementNS(String arg0, String arg1) throws DOMException {
        return this.getUnderlyingDocument().createElementNS(arg0, arg1);
    }

    /**
     *
     * @see Document#createEntityReference(java.lang.String)
     */
    @Override
    public EntityReference createEntityReference(String arg0) throws DOMException {
        return this.getUnderlyingDocument().createEntityReference(arg0);
    }

    /**
     *
     * @see Document#createProcessingInstruction(java.lang.String, java.lang.String)
     */
    @Override
    public ProcessingInstruction createProcessingInstruction(
            String arg0, String arg1) throws DOMException {
        return this.getUnderlyingDocument().createProcessingInstruction(arg0, arg1);
    }

    /**
     *
     * @see Document#createTextNode(java.lang.String)
     */
    @Override
    public Text createTextNode(String arg0) {
        return this.getUnderlyingDocument().createTextNode(arg0);
    }

    /**
     *
     * @see Document#getDoctype()
     */
    @Override
    public DocumentType getDoctype() {
        return this.getUnderlyingDocument().getDoctype();
    }

    /**
     * We need to wrap the document element with a composite element to ensure
     * that we only call composite methods on the root element.  This avoids a
     * class cast exception coming from an underlying implementation making
     * assumptions about the objects being passed to it.
     * @see Document#getDocumentElement()
     */
    @Override
    public Element getDocumentElement() {
        return this.getUnderlyingDocument().getDocumentElement();
    }

    /**
     *
     * @see Document#getDocumentURI()
     */
    @Override
    public String getDocumentURI() {
        return this.getUnderlyingDocument().getDocumentURI();
    }

    /**
     *
     * @see Document#getDomConfig()
     */
    @Override
    public DOMConfiguration getDomConfig() {
        return this.getUnderlyingDocument().getDomConfig();
    }

    /**
     *
     * @see Document#getElementById(java.lang.String)
     */
    @Override
    public Element getElementById(String arg0) {
        return this.getUnderlyingDocument().getElementById(arg0);
    }

    /**
     *
     * @see Document#getElementsByTagName(java.lang.String)
     */
    @Override
    public NodeList getElementsByTagName(String arg0) {
        return this.getUnderlyingDocument().getElementsByTagName(arg0);
    }

    /**
     *
     * @see Document#getElementsByTagNameNS(java.lang.String, java.lang.String)
     */
    @Override
    public NodeList getElementsByTagNameNS(String arg0, String arg1) {
        return this.getUnderlyingDocument().getElementsByTagNameNS(arg0, arg1);
    }

    /**
     *
     * @see Document#getImplementation()
     */
    @Override
    public DOMImplementation getImplementation() {
        return this.getUnderlyingDocument().getImplementation();
    }

    /**
     *
     * @see Document#getInputEncoding()
     */
    @Override
    public String getInputEncoding() {
        return this.getUnderlyingDocument().getInputEncoding();
    }

    /**
     *
     * @see Document#getStrictErrorChecking()
     */
    @Override
    public boolean getStrictErrorChecking() {
        return this.getUnderlyingDocument().getStrictErrorChecking();
    }

    /**
     *
     * @see Document#getXmlEncoding()
     */
    @Override
    public String getXmlEncoding() {
        return this.getUnderlyingDocument().getXmlEncoding();
    }

    /**
     *
     * @see Document#getXmlStandalone()
     */
    @Override
    public boolean getXmlStandalone() {
        return this.getUnderlyingDocument().getXmlStandalone();
    }

    /**
     *
     * @see Document#getXmlVersion()
     */
    @Override
    public String getXmlVersion() {
        return this.getUnderlyingDocument().getXmlVersion();
    }

    /**
     * If arg0 is a CompositeNode, then we need to use the underlying node
     * as it is the one with the same implementation as the document.
     *
     * @see Document#importNode(org.w3c.dom.Node, boolean)
     */
    @Override
    public Node importNode(Node arg0, boolean arg1) throws DOMException {
        return this.getUnderlyingDocument().importNode(
                CompositeNode.getRealNode(arg0), arg1);
    }

    /**
     *
     * @see Document#normalizeDocument()
     */
    @Override
    public void normalizeDocument() {
        this.getUnderlyingDocument().normalizeDocument();
    }

    /**
     * If arg0 is a CompositeNode, then we need to use the underlying node
     * as it is the one with the same implementation as the document.
     *
     * @see Document#renameNode(org.w3c.dom.Node, java.lang.String, java.lang.String)
     */
    @Override
    public Node renameNode(Node arg0, String arg1, String arg2) throws DOMException {
        return this.getUnderlyingDocument().renameNode(
                CompositeNode.getRealNode(arg0), arg1, arg2);
    }

    /**
     *
     * @see Document#setDocumentURI(java.lang.String)
     */
    @Override
    public void setDocumentURI(String arg0) {
        this.getUnderlyingDocument().setDocumentURI(arg0);
    }

    /**
     *
     * @see Document#setStrictErrorChecking(boolean)
     */
    @Override
    public void setStrictErrorChecking(boolean arg0) {
        this.getUnderlyingDocument().setStrictErrorChecking(arg0);
    }

    /**
     *
     * @see Document#setXmlStandalone(boolean)
     */
    @Override
    public void setXmlStandalone(boolean arg0) throws DOMException {
        this.getUnderlyingDocument().setXmlStandalone(arg0);
    }

    /**
     *
     * @see Document#setXmlVersion(java.lang.String)
     */
    @Override
    public void setXmlVersion(String arg0) throws DOMException {
        this.getUnderlyingDocument().setXmlVersion(arg0);
    }
}
